#ifndef GSTVIDEORECEIVER_H
#define GSTVIDEORECEIVER_H

#include "IVideoReceiver.h"

#include <QQueue>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>

#include <QTimer>
#include <QLoggingCategory>

#include <gst/gst.h>


Q_DECLARE_LOGGING_CATEGORY(VideoReceiverLog)

class Worker : public QThread
{
    Q_OBJECT

public:
    using Task = std::function<void()>;
    
    // 添加构造函数和析构函数
    Worker() : _shutdown(false) {}
    ~Worker() {
        shutdown();
    }

    bool needDispatch() const {
        // 若当前不在Worker线程运行
        return QThread::currentThread() != this;
    }

    // 发送任务函数到线程
    void dispatch(Task t) {
        // 任务线程可用
        if (isRunning()) {
            // 入队并通知执行
            QMutexLocker lock(&_taskQueueSync);
            _taskQueue.enqueue(std::move(t));
            _taskQueueUpdate.wakeOne();
        }
    }

    // 退出线程
    void shutdown() {
        // 设置新值并返回旧值 (保证只执行一次)
        if (!_shutdown.fetchAndStoreRelaxed(true)) {
            // 先检查线程状态
            if (isRunning()) {
                // 任务全部清空后再退出，防止资源未释放
                if (needDispatch()) {
                    dispatch([this] {
                        _taskQueueUpdate.wakeOne();
                    });
                } else {
                    _taskQueueUpdate.wakeOne();
                }
                QThread::wait();
            }
        }
    }

protected:
    void run() override {
        while(!_shutdown.load()) {
            Task task;
            {
                QMutexLocker locker(&_taskQueueSync);
                // 任务列表为空
                if (_taskQueue.isEmpty()) {
                    if (_shutdown.load()) {
                        break;
                    }
                    // 阻塞线程直至新任务入列
                    _taskQueueUpdate.wait(&_taskQueueSync);
                }
                if (!_taskQueue.isEmpty()) {
                    task = _taskQueue.dequeue();
                }
            }
            // 执行任务
            if (task) {
                try {
                    task();
                } catch (const std::exception& e) {
                    qWarning() << "Worker task exception:" << e.what();
                } catch (...) {
                    qWarning() << "Worker task unknown exception";
                }
            }
        }
    }

private:
    QWaitCondition      _taskQueueUpdate;
    QMutex              _taskQueueSync;
    QQueue<Task>        _taskQueue;
    QAtomicInteger<bool> _shutdown { false };
};

class GstVideoReceiver final : public IVideoReceiver
{
    Q_OBJECT

public:
    explicit GstVideoReceiver   (QObject *parent = nullptr);
    ~GstVideoReceiver           (void);

    bool decoding() override { return _decoding; }

public slots:
    // IVideoReceiver interface
    void init           ()                          override;
    void start          (const QString &uri, unsigned int timeout, int buffer, int cacheTime) override;
    void stop           ()                          override;
    void restart        (unsigned long waitTime)    override;
    void startDecoding  (void *sink)                override;
    void stopDecoding   ()                          override;

private slots:
    void _startVideo    (void);
    void _stopVideo     (void);

    void _watchDog      (void);
    void _handleEos     (void);

private:
    GstElement *_makeSource     (const QString &uri);
    GstElement *_makeDecoder    (GstCaps *caps = nullptr, GstElement *videoSink = nullptr);

    bool _initDecoder           (void);
    void _releaseDecoder        (void);

    void _onNewSourcePad        (GstPad *pad);
    void _onNewDecoderPad       (GstPad *pad);

    bool _addDecoder            (GstElement *src);
    bool _addVideoSink          (GstPad *pad);

    void _noteTeeFrame          (void);
    void _noteVideoSinkFrame    (void);
    void _noteEndOfStream       (void);

    bool _needDispatch          (void);
    void _dispatchSignal        (std::function<void()> emitter);

    bool _checkUrlValid         (const QString &url);

    // GSteamer Callback Function
    static gboolean _onBusMessage   (GstBus *bus, GstMessage *message, gpointer user_data);
    static void     _onNewPad       (GstElement *element, GstPad *pad, gpointer data);
    static void     _onSourcePad    (GstElement *src, GstPad *pad, gpointer data);
    static void     _wrapWithGhostPad(GstElement *element, GstPad *pad, gpointer data);
    static void     _linkPad        (GstElement *element, GstPad *pad, gpointer data);
    static gboolean _padProbe       (GstElement *element, GstPad *pad, gpointer user_data);
    static gboolean _filterParserCaps(GstElement *bin, GstPad *pad, GstElement *element, GstQuery *query, gpointer data);
    static GstPadProbeReturn _videoSinkProbe(GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
    static GstPadProbeReturn _eosProbe      (GstPad *pad, GstPadProbeInfo *info, gpointer user_data);
    static GstPadProbeReturn _keyframeWatch (GstPad *pad, GstPadProbeInfo *info, gpointer user_data);

private:
    GstElement *_pipeLine       = NULL;
    GstElement *_source         = NULL;
    GstElement *_decoder        = NULL;
    GstElement *_decoderValue   = NULL;
    GstElement *_videoSink      = NULL;

private:
    bool _isStreaming           = false;
    bool _isRemovingDecoder     = false;
    bool _isResetVideoSink      = false;
    bool _isEndOfStream         = false;
    bool _isDeadError           = false;

    unsigned _timeout           = 0;
    int _decoderTimeoutCnt      = 0;
    int _buffer                 = -1;
    int _cacheTime              = -1;

    gulong _videoSinkProbeId     = 0;
    quint64 _lastSourceFrameTime = 0;
    quint64 _lastVideoFrameTime  = 0;

    QString _sourceUri;

    QTimer _watchDogTimer;

    Worker _worker;
    uint32_t _signalDepth       = 0;
};

#endif // GSTVIDEORECEIVER_H
